package com.jctrip.hotel.source.service.CNBooking.impl;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.google.common.collect.Lists;
import com.jctrip.hotel.domain.entity.*;
import com.jctrip.hotel.source.service.basic.*;
import com.jctrip.hotel.source.xxl.cnbe.thread.*;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.jctrip.hotel.common.constant.RedisKeyConstant;
import com.jctrip.hotel.common.constant.XxlJobNameConstant;
import com.jctrip.hotel.common.enums.SourceEnum;
import com.jctrip.hotel.common.threadpool.XxlJobThreadPool;
import com.jctrip.hotel.common.util.RedisUtil;
import com.jctrip.hotel.domain.dao.HtHotelStdInfoDao;
import com.jctrip.hotel.source.model.CnBooking.CityDataDTO;
import com.jctrip.hotel.source.model.CnBooking.IdentityInfoDTO;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryHotelResp.Hotel;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryHotelResp.HotelsData;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.Rate;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.RatePlan;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.RatePlans;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.Rates;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomStatusResp.RoomStatusHotel;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomTypeResp.Hotels;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomTypeResp.RoomTypeData;
import com.jctrip.hotel.source.model.CnBooking.resp.QueryRoomTypeResp.Rooms;
import com.jctrip.hotel.source.service.CNBooking.CNBookingService;
import com.jctrip.hotel.source.service.CNBooking.CNBookingTaskService;

import lombok.extern.slf4j.Slf4j;

/**
 * @Author henry
 * @Date 2023/7/2 15:36
 * @Description
 */
@Service
@Slf4j
public class CNBookingTaskServiceImpl implements CNBookingTaskService {
    private static final long PUSH_EXPIRY = 1 * 60;
    private final static XxlJobThreadPool syncInfoListTaskPool =
            new XxlJobThreadPool(XxlJobNameConstant.SYNE_CNBE_CITY_TASK_POP);
    private final static XxlJobThreadPool syncXgAmInfoTaskPool =
            new XxlJobThreadPool(XxlJobNameConstant.SYNC_CNBE_XG_AM_INFO_POP);
    private final static XxlJobThreadPool syncRatesPlanTaskPool =
            new XxlJobThreadPool(XxlJobNameConstant.SYNE_CNBE_RATES_TASK_POP);
    private final static XxlJobThreadPool syncPartRatesTaskPool =
            new XxlJobThreadPool(XxlJobNameConstant.SYNC_CNBE_PART_RATES_POP);
    private final static XxlJobThreadPool syncXgAmRatesTaskPool =
            new XxlJobThreadPool(XxlJobNameConstant.SYNC_CNBE_XG_AM_RATES_POP);
    private final static XxlJobThreadPool syncXgAmAllRatesTaskPool =
            new XxlJobThreadPool(XxlJobNameConstant.SYNC_CNBE_XG_AM_ALL_RATES_POP);
    private final static List<String> countryIds = Lists.newArrayList("0007", "4231", "0120", "0029", "0026", "3785", "0023", "3436", "0012", "0003", "0080", "0008",
            "0046", "0090", "0076", "0017", "0069", "0024", "113", "0016", "0018", "122", "0028", "0043", "0019", "12103", "0035", "0061", "0083", "0032", "3957",
            "0065", "0009", "0014", "0020", "0057", "0075", "0094", "0054", "0093", "4473", "0081", "0059", "0027", "0098", "0062", "0073", "0063", "0099", "00109",
            "0030", "0064", "6561238", "0034", "0013", "0039", "0022", "121", "00100", "4458", "0092", "6350", "0031", "0033", "1254", "4448", "0015", "0011", "9907",
            "0006", "0091", "0005", "00105", "0010");
    @Autowired
    private CNBookingService cnBookingService;
    @Autowired
    private RedisUtil redisUtil;
    @Resource
    private HtHotelStdInfoService htHotelStdInfoService;
    @Resource
    private HtHotelStdInfoDao htHotelStdInfoDao;
    @Resource
    private HtPriceCalendarService htPriceCalendarService;
    @Resource
    private HtRoomTypeStdInfoService htRoomTypeStdInfoService;
    @Resource
    private HtRoomTypeStdInfoCnbeService htRoomTypeStdInfoCnbeService;
    @Resource
    private HtPriceCalendarCnbeService htPriceCalendarCnbeService;
    @Resource
    private HtPriceCalendarCnbeintService htPriceCalendarCnbeintService;

    @Override
    public void syncCNBookingCityListTaskPush(IdentityInfoDTO infoDTO, List<String> countryIds) {
        try {
            if (redisUtil.setENx(RedisKeyConstant.HOTEL_MAIN_CNBE_CITYID_PUSH, PUSH_EXPIRY,
                    RedisKeyConstant.HOTEL_MAIN_CNBE_CITYID_PUSH)) {
                List<CityDataDTO> cityData = cnBookingService.getCityData(infoDTO);
                Optional.ofNullable(cityData).orElse(new ArrayList<>()).forEach(cityDataDTO -> {
                    if (CollectionUtils.isEmpty(countryIds)) {
                        redisUtil.leftPush(RedisKeyConstant.HOTEL_MAIN_CNBE_CITYID_LIST, cityDataDTO);
                    } else if (countryIds.contains(cityDataDTO.getCountryId())) {
                        redisUtil.leftPush(RedisKeyConstant.HOTEL_MAIN_CNBE_CITYID_LIST, cityDataDTO);
                    }
                });
                log.info("syncCNBookingCityListTaskPush city size: {} ", cityData.size());
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void syncCNBEHotelInfoListTaskFastPop(IdentityInfoDTO infoDTO) {
        syncInfoListTaskPool.execute(() -> new SyncCNBEHotelInfoListTaskThread(infoDTO));
    }

    @Override
    public void syncCNBEHotelInfoListTaskPop(String countryId, String provinceId, String cityId,
                                             IdentityInfoDTO infoDTO) {
        // 获取酒店
        Integer page = 1;
        while (true) {
            HotelsData hotelsData =
                    cnBookingService.queryHotel(countryId, provinceId, cityId, infoDTO.getLang(), page, 100,
                            null, infoDTO);
            if (hotelsData == null || hotelsData.getHotels() == null || CollectionUtils.isEmpty(
                    hotelsData.getHotels().getHotelList())) {
                break;
            }
            HotelsData tempHotelsData = null;
            if (!"0001".equals(countryId)) {
                tempHotelsData = cnBookingService.queryHotel(countryId, provinceId, cityId, "GB", page, 100,
                        null, infoDTO);
            }
            Integer hotelCount = hotelsData.getHotels().getHotelCount();
            int totalPage = (int) Math.ceil(hotelCount * 1.0 / 100);
            // 已经处理过最后一页数据了
            if (totalPage < page) {
                break;
            }
            List<Hotel> hotelList = hotelsData.getHotels().getHotelList();
            for (Hotel hotel : hotelList) {
                HtHotelStdInfo htHotelStdInfo = buildHotelBaseDate(hotel, tempHotelsData);
                htHotelStdInfoService.saveOrUpdate(htHotelStdInfo,
                        new LambdaQueryWrapper<HtHotelStdInfo>().eq(HtHotelStdInfo::getHotelId,
                                htHotelStdInfo.getHotelId())
                                .eq(HtHotelStdInfo::getSourceCode, "0001".equals(htHotelStdInfo.getCountry()) ? SourceEnum.CNBE.getCode() : SourceEnum.CNBEINT.getCode()));
                // 查询房型
                RoomTypeData roomTypeData =
                        cnBookingService.queryRoomType(countryId, hotel.getHotelId(), infoDTO.getLang(), infoDTO);
                Hotels hotels = Optional.ofNullable(roomTypeData.getHotels()).orElse(new Hotels());
                Rooms rooms =
                        Optional.ofNullable(hotels.getHotelList()).orElse(new ArrayList<>()).get(0).getRooms();
                // 房型落库
                Optional.ofNullable(Optional.ofNullable(rooms).orElse(new Rooms()).getRoom())
                        .orElse(new ArrayList<>())
                        .forEach(room -> {
                            if (room == null) {
                                return;
                            }
                            HtRoomTypeStdInfo roomTypeStdInfo = new HtRoomTypeStdInfo();
                            roomTypeStdInfo.setHotelId(htHotelStdInfo.getHotelId());
                            roomTypeStdInfo.setRoomTypeCode(room.getRoomId());
                            roomTypeStdInfo.setRoomTypeName(room.getRoomName().replaceAll("&lt;", "<" )
                                    .replaceAll("&gt;", ">" ));
                            //                            roomTypeStdInfo.setMaxCheckIn(room.getMaxAdult());
                            roomTypeStdInfo.setFloor(room.getFloor());
                            Integer bedType = null;
                            if (StringUtils.isNotBlank(room.getBedType())) {
                                switch (room.getBedType()) {
                                    case "大床":
                                        bedType = 0;
                                        break;
                                    case "单人床":
                                        bedType = 1;
                                        break;
                                    case "双床":
                                        bedType = 2;
                                        break;
                                    case "特大床":
                                        bedType = 3;
                                        break;
                                    case "双人床":
                                        bedType = 4;
                                        break;
                                    default:
                                        bedType = 5;
                                        break;
                                }
                            }
                            roomTypeStdInfo.setBedType(bedType);
                            htRoomTypeStdInfoService.saveOrUpdate(roomTypeStdInfo,
                                    new LambdaQueryWrapper<HtRoomTypeStdInfo>().eq(HtRoomTypeStdInfo::getHotelId,
                                            roomTypeStdInfo.getHotelId())
                                            .eq(HtRoomTypeStdInfo::getRoomTypeCode,
                                                    roomTypeStdInfo.getRoomTypeCode()));
                        });
            }
            page++;
        }
    }

    @Override
    public void syncCNBERatesTaskFastPush() {
        try {
            List<HtHotelStdInfo> htHotelStdInfos = htHotelStdInfoDao.selectList(
                    new LambdaQueryWrapper<HtHotelStdInfo>()
                            .eq(HtHotelStdInfo::getSourceCode, SourceEnum.CNBE.getCode())
                            .eq(HtHotelStdInfo::getSourceStatus, 1)
                            .eq(HtHotelStdInfo::getBookStatus, 1));
            if (!CollectionUtils.isEmpty(htHotelStdInfos)
                    && redisUtil.lGetListSize(RedisKeyConstant.HOTEL_MAIN_CNBE_RATE_INNID_LIST) <= 0) {
                redisUtil.leftPushAll(RedisKeyConstant.HOTEL_MAIN_CNBE_RATE_INNID_LIST,
                        htHotelStdInfos.stream().map(HtHotelStdInfo::getHotelId).collect(Collectors.toList()));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void syncCNBERatesTaskFastPop(IdentityInfoDTO infoDTO) {
        syncRatesPlanTaskPool.execute(() -> new SyncCNBERatesTaskThread(infoDTO));
    }

    @Override
    public void syncCNBEHotelRatePlanTaskPop(String countryId, String currency, String hotelId, String start,
                                             String end, IdentityInfoDTO infoDTO) {
        RoomStatusHotel roomStatusHotel =
                cnBookingService.queryRoomStatus(countryId, currency, cnBookingService.parseHotelId(hotelId), null,
                        null, start, end, infoDTO);
        List<QueryRoomStatusResp.Hotel> hotelList =
                Optional.ofNullable(roomStatusHotel.getHotels()).orElse(new QueryRoomStatusResp.Hotels())
                        .getHotel();
        QueryRoomStatusResp.Rooms rooms =
                Optional.ofNullable(hotelList).orElse(new ArrayList<>()).get(0).getRooms();
        String ratePlanId = infoDTO.getRatePlanId();
        Optional.ofNullable(rooms.getRoom()).orElse(new ArrayList<>()).forEach(roomStatus -> {
            // 获取指定的价格计划
            RatePlan ratePlan =
                    Optional.ofNullable(roomStatus.getRatePlans()).orElse(new RatePlans()).getRatePlan().stream()
                            .filter(rate -> ratePlanId.equals(rate.getRatePlanId())).findFirst().get();
            // 获取房态
            Rate roomRate = Optional.ofNullable(roomStatus.getRates()).orElse(new Rates()).getRate().stream()
                    .filter(rate -> ratePlanId.equals(rate.getRatePlanId())).findFirst().get();
            Optional.ofNullable(roomRate).orElse(new Rate()).getPriceAndStatus().getPriceAndStatu()
                    .forEach(priceAndStatus -> {
                        // 保存房态
                        if (priceAndStatus == null) {
                            return;
                        }
                        HtPriceCalendar htPriceCalendar = new HtPriceCalendar();
                        htPriceCalendar.setHotelId(hotelId);
                        htPriceCalendar.setRoomTypeCode(roomStatus.getRoomId());
                        htPriceCalendar.setRoomTypeName(roomStatus.getRoomName());
                        try {
                            htPriceCalendar.setCalDate(
                                    new SimpleDateFormat("yyyy/MM/dd" ).parse(priceAndStatus.getDate()));
                            htPriceCalendar.setQuota(Integer.valueOf(priceAndStatus.getCount()));
                            htPriceCalendar.setCurrencyCode(priceAndStatus.getCurrency());
                            String breakfast = ratePlan.getBreakfast();
                            Integer breakfastCount = 0;
                            if (StringUtils.isNotBlank(breakfast)) {
                                switch (breakfast) {
                                    case "011001":
                                        breakfastCount = 0;
                                        break;
                                    case "011002":
                                        breakfastCount = 1;
                                        break;
                                    case "011003":
                                        breakfastCount = 2;
                                        break;
                                    default:
                                        break;
                                }
                            }
                            htPriceCalendar.setBreakfastCount(breakfastCount);
                            htPriceCalendar.setPurchasePrice(new BigDecimal(priceAndStatus.getPrice()));
                            htPriceCalendar.setLastCancelTime(
                                    new SimpleDateFormat("yyyy/MM/dd HH:mm:ss" )
                                            .parse(priceAndStatus.getLastCancelTime()));
                            // 保存房态信息
                            htPriceCalendarService.saveOrUpdate(htPriceCalendar,
                                    new LambdaQueryWrapper<HtPriceCalendar>().eq(HtPriceCalendar::getHotelId, hotelId)
                                            .eq(HtPriceCalendar::getRoomTypeCode, htPriceCalendar.getRoomTypeCode())
                                            .eq(HtPriceCalendar::getCalDate, htPriceCalendar.getCalDate()));
                        } catch (Exception e) {
                            log.error("syncCNBEHotelRatePlanTaskPop error", e);
                        }
                    });
        });
    }

    @Override
    public void syncCNBERatesTaskPush() {
        try {
            List<String> hotels = htRoomTypeStdInfoCnbeService.list(new QueryWrapper<HtRoomTypeStdInfoCnbe>()
                    .select("DISTINCT(hotel_id)" )).stream().map(HtRoomTypeStdInfoCnbe::getHotelId).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(hotels)
                    && redisUtil.lGetListSize(RedisKeyConstant.HOTEL_CNBE_PART_RATES_LIST) <= 0) {
                redisUtil.leftPushAll(RedisKeyConstant.HOTEL_CNBE_PART_RATES_LIST, new ArrayList<>(hotels));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void syncCNBERatesTaskPop(IdentityInfoDTO infoDTO) {
        syncPartRatesTaskPool.execute(() -> new SyncCNBEPartRatesTaskThread(infoDTO));
    }

    @Override
    public void syncCNBERatesTask(String currency, String hotelId, String start, String end, IdentityInfoDTO infoDTO) {
        updateQuota(hotelId);
        RoomStatusHotel roomStatusHotel =
                cnBookingService.queryRoomStatus(null, currency, cnBookingService.parseHotelId(hotelId), null,
                        null, start, end, infoDTO);
        List<QueryRoomStatusResp.Hotel> hotelList =
                Optional.ofNullable(roomStatusHotel.getHotels())
                        .orElse(new QueryRoomStatusResp.Hotels()).getHotel();
        if (CollectionUtils.isEmpty(hotelList)) {
            return;
        }
        QueryRoomStatusResp.Rooms rooms = hotelList.get(0).getRooms();

        Optional.ofNullable(rooms.getRoom()).orElse(new ArrayList<>()).forEach(roomStatus -> {
            List<HtRoomTypeStdInfoCnbe> htRoomTypeStdInfoCnbes = htRoomTypeStdInfoCnbeService.list(new LambdaQueryWrapper<HtRoomTypeStdInfoCnbe>()
                    .eq(HtRoomTypeStdInfoCnbe::getHotelId, hotelId).eq(HtRoomTypeStdInfoCnbe::getRoomTypeCode, roomStatus.getRoomId()));
            if (CollectionUtils.isEmpty(htRoomTypeStdInfoCnbes)) {
                return;
            }
            htRoomTypeStdInfoCnbes.forEach(htCnbeRoomTypeStdInfo -> {
                if (StringUtils.isBlank(htCnbeRoomTypeStdInfo.getRateplanId())) {
                    return;
                }
                // 获取指定的价格计划
//                RatePlan ratePlan =
//                        Optional.ofNullable(roomStatus.getRatePlans()).orElse(new RatePlans()).getRatePlan().stream()
//                                .filter(rate -> htCnbeRoomTypeStdInfo.getRateplanId().equals(rate.getRatePlanId())).findFirst().orElse(null);
                List<RatePlan> ratePlanList = Optional.ofNullable(roomStatus.getRatePlans()).orElse(new RatePlans()).getRatePlan();
                if (CollectionUtils.isEmpty(ratePlanList)) {
                    return;
                }
                RatePlan ratePlan = ratePlanList.stream()
                        .filter(rate -> htCnbeRoomTypeStdInfo.getRateplanId().equals(rate.getRatePlanId())).findFirst().orElse(null);

                if (null == ratePlan) {
                    return;
                }
                // 获取房态
                Rate roomRate = Optional.ofNullable(roomStatus.getRates()).orElse(new Rates()).getRate().stream()
                        .filter(rate -> htCnbeRoomTypeStdInfo.getRateplanId().equals(rate.getRatePlanId())).findFirst().orElse(null);
                if (null == roomRate || null == roomRate.getPriceAndStatus() || null == roomRate.getPriceAndStatus().getPriceAndStatu()) {
                    return;
                }
                QueryRoomStatusResp.PriceAndStatus priceAndStatusObj = Optional.ofNullable(roomRate).orElse(new QueryRoomStatusResp.Rate())
                        .getPriceAndStatus();
                if (priceAndStatusObj == null) {
                    return;
                }
                Optional.ofNullable(priceAndStatusObj.getPriceAndStatu())
                        .orElse(new ArrayList<>())
                        .forEach(priceAndStatus -> {
                            // 保存房态
                            if (priceAndStatus == null) {
                                return;
                            }
                            HtPriceCalendarCnbe htPriceCalendarCnbe = new HtPriceCalendarCnbe();
                            htPriceCalendarCnbe.setHotelId(hotelId);
                            htPriceCalendarCnbe.setRoomTypeCode(roomStatus.getRoomId());
                            htPriceCalendarCnbe.setRoomTypeName(roomStatus.getRoomName());
                            try {
                                htPriceCalendarCnbe.setCalDate(
                                        new SimpleDateFormat("yyyy/MM/dd" ).parse(priceAndStatus.getDate()));
                                // 龙腾库存还有冻结状态 026001为开启 否则库存直接变0
                                htPriceCalendarCnbe.setQuota("026001".equals(priceAndStatus.getStatu()) ?
                                        Integer.parseInt(priceAndStatus.getCount()) : 0);
                                htPriceCalendarCnbe.setCurrencyCode(priceAndStatus.getCurrency());
                                String breakfast = ratePlan.getBreakfast();
                                Integer breakfastCount = 0;
                                if (StringUtils.isNotBlank(breakfast)) {
                                    switch (breakfast) {
                                        case "011001":
                                            breakfastCount = 0;
                                            break;
                                        case "011002":
                                            breakfastCount = 1;
                                            break;
                                        case "011003":
                                            breakfastCount = 2;
                                            break;
                                        default:
                                            break;
                                    }
                                }
                                htPriceCalendarCnbe.setProductId(htCnbeRoomTypeStdInfo.getRateplanId());
                                htPriceCalendarCnbe.setBreakfastCount(breakfastCount);
                                htPriceCalendarCnbe.setPurchasePrice(new BigDecimal(priceAndStatus.getPrice()));
                                htPriceCalendarCnbe.setLastCancelTime(
                                        new SimpleDateFormat("yyyy/MM/dd HH:mm:ss" ).parse(
                                                priceAndStatus.getLastCancelTime()));
                                // 保存房态信息
                                htPriceCalendarCnbeService.saveOrUpdate(htPriceCalendarCnbe,
                                        new LambdaQueryWrapper<HtPriceCalendarCnbe>().eq(HtPriceCalendarCnbe::getHotelId, hotelId)
                                                .eq(HtPriceCalendarCnbe::getRoomTypeCode, htPriceCalendarCnbe.getRoomTypeCode())
                                                .eq(HtPriceCalendarCnbe::getProductId, htPriceCalendarCnbe.getProductId())
                                                .eq(HtPriceCalendarCnbe::getCalDate, htPriceCalendarCnbe.getCalDate()));
                            } catch (Exception e) {
                                log.error("syncCNBEHotelRatePlanTaskPop error", e);
                            }
                        });
            });
        });
    }

    /**
     * 获取香港和澳门的酒店id
     */
    @Override
    public void syncCNBEXgAmRatesTaskPush() {
        try {
            if (redisUtil.setENx(RedisKeyConstant.HOTEL_CNBE_XG_AM_RATES_PUSH, PUSH_EXPIRY
                    , RedisKeyConstant.HOTEL_CNBE_XG_AM_RATES_PUSH)) {
                //国家id ： 香港 0002， 澳门  0004
                List<HtHotelStdInfo> htHotelStdInfos = htHotelStdInfoDao.selectList(
                        new LambdaQueryWrapper<HtHotelStdInfo>()
                                .eq(HtHotelStdInfo::getSourceCode, SourceEnum.CNBEINT.getCode())
                                .eq(HtHotelStdInfo::getSourceStatus, 1)
                                .eq(HtHotelStdInfo::getBookStatus, 1)
                                .in(HtHotelStdInfo::getCountry, countryIds));
                if (!CollectionUtils.isEmpty(htHotelStdInfos)
                        && redisUtil.lGetListSize(RedisKeyConstant.HOTEL_CNBE_XG_AM_RATES_LIST) <= 0) {
                    redisUtil.leftPushAll(RedisKeyConstant.HOTEL_CNBE_XG_AM_RATES_LIST,
                            htHotelStdInfos.stream().map(HtHotelStdInfo::getHotelId).collect(Collectors.toList()));
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void syncCNBEXgAmRatesTaskPop(IdentityInfoDTO infoDTO) {
        syncXgAmRatesTaskPool.execute(() -> new SyncCNBEXgAmRatesTaskThread(infoDTO));
    }

    @Override
    public void syncCNBEXgAmRatePlaneTask(String countryId, String currency, String hotelId, String start, String end, IdentityInfoDTO infoDTO) {
        RoomStatusHotel roomStatusHotel =
                cnBookingService.queryRoomStatus(countryId, currency, cnBookingService.parseHotelId(hotelId), null,
                        null, start, end, infoDTO);
        List<QueryRoomStatusResp.Hotel> hotelList =
                Optional.ofNullable(roomStatusHotel.getHotels()).orElse(new QueryRoomStatusResp.Hotels())
                        .getHotel();
        QueryRoomStatusResp.Rooms rooms =
                Optional.ofNullable(hotelList).orElse(new ArrayList<>()).get(0).getRooms();
        if (null == rooms) {
            return;
        }
        Optional.ofNullable(rooms.getRoom()).orElse(new ArrayList<>()).forEach(roomStatus -> {
            // 遍历本地所有酒店房型的价格计划
            List<HtRoomTypeStdInfoCnbe> htRoomTypeStdInfoCnbes = htRoomTypeStdInfoCnbeService.list(new LambdaQueryWrapper<HtRoomTypeStdInfoCnbe>()
                    .eq(HtRoomTypeStdInfoCnbe::getHotelId, hotelId)
                    .eq(HtRoomTypeStdInfoCnbe::getRoomTypeCode, roomStatus.getRoomId()));

            if (CollectionUtils.isEmpty(htRoomTypeStdInfoCnbes)) {
                return;
            }

//            if (CollectionUtils.isEmpty(roomStatus.getRatePlans().getRatePlan())
//                    || CollectionUtils.isEmpty(roomStatus.getRates().getRate())) {
//                updateQuota(hotelId, roomStatus.getRoomId(), null);
//                return;
//            }

            htRoomTypeStdInfoCnbes.forEach(htCnbeRoomTypeStdInfo -> {
                if (StringUtils.isBlank(htCnbeRoomTypeStdInfo.getRateplanId())) {
                    return;
                }

                // 获取指定的价格计划
                RatePlan ratePlan =
                        Optional.ofNullable(roomStatus.getRatePlans()).orElse(new RatePlans()).getRatePlan().stream()
                                .filter(rate -> htCnbeRoomTypeStdInfo.getRateplanId().equals(rate.getRatePlanId())).findFirst().orElse(null);
                if (null == ratePlan) {
//                    updateQuota(htCnbeRoomTypeStdInfo.getHotelId(), htCnbeRoomTypeStdInfo.getRoomTypeCode(), htCnbeRoomTypeStdInfo.getRateplanId());
                    return;
                }
                // 获取房态
                Rate roomRate = Optional.ofNullable(roomStatus.getRates()).orElse(new Rates()).getRate().stream()
                        .filter(rate -> htCnbeRoomTypeStdInfo.getRateplanId().equals(rate.getRatePlanId())).findFirst().orElse(null);
//                if (null == roomRate || null == roomRate.getPriceAndStatus() || null == roomRate.getPriceAndStatus().getPriceAndStatu()) {
//                    updateQuota(htCnbeRoomTypeStdInfo.getHotelId(), htCnbeRoomTypeStdInfo.getRoomTypeCode(), htCnbeRoomTypeStdInfo.getRateplanId());
//                    return;
//                }
                QueryRoomStatusResp.PriceAndStatus priceAndStatusObj = Optional.ofNullable(roomRate).orElse(new QueryRoomStatusResp.Rate())
                        .getPriceAndStatus();
                if (priceAndStatusObj == null) {
                    return;
                }

                Optional.ofNullable(priceAndStatusObj.getPriceAndStatu())
                        .orElse(new ArrayList<>())
                        .forEach(priceAndStatus -> {
                            // 保存房态
                            if (priceAndStatus == null) {
                                return;
                            }
                            if (priceAndStatus.getDate().replaceAll("/", "-" ).equals(end)) {
                                return;
                            }
                            HtPriceCalendarCnbe htPriceCalendarCnbe = new HtPriceCalendarCnbe();
                            htPriceCalendarCnbe.setHotelId(hotelId);
                            htPriceCalendarCnbe.setRoomTypeCode(roomStatus.getRoomId());
                            htPriceCalendarCnbe.setRoomTypeName(roomStatus.getRoomName().replaceAll("&lt;", "<" )
                                    .replaceAll("&gt;", ">" ));
                            try {
                                htPriceCalendarCnbe.setCalDate(
                                        new SimpleDateFormat("yyyy/MM/dd" ).parse(priceAndStatus.getDate()));
                                // 库存,026001冻结状态 026001为开启
                                htPriceCalendarCnbe.setQuota("026001".equals(priceAndStatus.getStatu()) ?
                                        Integer.parseInt(priceAndStatus.getCount()) : 0);
                                htPriceCalendarCnbe.setCurrencyCode(priceAndStatus.getCurrency());
                                String breakfast = ratePlan.getBreakfast();
                                int breakfastCount = 0;
                                if (StringUtils.isNotBlank(breakfast)) {
                                    switch (breakfast) {
                                        case "011001":
                                            breakfastCount = 0;
                                            break;
                                        case "011002":
                                            breakfastCount = 1;
                                            break;
                                        case "011003":
                                            breakfastCount = 2;
                                            break;
                                        default:
                                            break;
                                    }
                                }
                                htPriceCalendarCnbe.setProductId(htCnbeRoomTypeStdInfo.getRateplanId());
                                htPriceCalendarCnbe.setBreakfastCount(breakfastCount);
                                htPriceCalendarCnbe.setPurchasePrice(new BigDecimal(priceAndStatus.getPrice()));
                                htPriceCalendarCnbe.setLastCancelTime(
                                        new SimpleDateFormat("yyyy/MM/dd HH:mm:ss" )
                                                .parse(priceAndStatus.getLastCancelTime()));
                                // 保存房态信息 todo 暂时去掉价格政策，后续更正
                                htPriceCalendarCnbeService.saveOrUpdate(htPriceCalendarCnbe,
                                        new LambdaQueryWrapper<HtPriceCalendarCnbe>().eq(HtPriceCalendarCnbe::getHotelId, hotelId)
                                                .eq(HtPriceCalendarCnbe::getRoomTypeCode, htPriceCalendarCnbe.getRoomTypeCode())
                                                .eq(HtPriceCalendarCnbe::getCalDate, htPriceCalendarCnbe.getCalDate()));
                            } catch (Exception e) {
                                log.error("syncCNBEXgAmRatePlaneTask error", e);
                            }
                        });
            });
        });
    }

    @Override
    public void syncCNBEXgAmInfoTaskPush() {
        try {
            if (redisUtil.setENx(RedisKeyConstant.HOTEL_CNBE_XG_AM_INFO_PUSH, PUSH_EXPIRY
                    , RedisKeyConstant.HOTEL_MAIN_GREENTREE_HOTELID_PUSH)) {
                List<HtHotelStdInfo> htHotelStdInfos = htHotelStdInfoDao.selectList(new LambdaQueryWrapper<HtHotelStdInfo>()
                        .eq(HtHotelStdInfo::getSourceCode, SourceEnum.CNBEINT.getCode())
                        .in(HtHotelStdInfo::getCountry, countryIds));
                if (!CollectionUtils.isEmpty(htHotelStdInfos)
                        && redisUtil.lGetListSize(RedisKeyConstant.HOTEL_CNBE_XG_AM_INFO_LIST) <= 0) {
                    redisUtil.leftPushAll(RedisKeyConstant.HOTEL_CNBE_XG_AM_INFO_LIST,
                            htHotelStdInfos.stream().map(HtHotelStdInfo::getHotelId).collect(Collectors.toList()));
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void syncCNBEXgAmInfoTaskPop(IdentityInfoDTO infoDTO) {
        syncXgAmInfoTaskPool.execute(() -> new SyncCNBEXgAmInfoTaskThread(infoDTO));
    }

    @Override
    public void syncCNBEXgAmInfoTask(String hotelId, IdentityInfoDTO infoDTO) {
        RoomTypeData roomTypeData = cnBookingService.queryRoomType(null, cnBookingService.parseHotelId(hotelId), infoDTO.getLang(), infoDTO);
        Hotels hotels = Optional.ofNullable(roomTypeData.getHotels()).orElse(new Hotels());
        Rooms rooms = Optional.ofNullable(hotels.getHotelList()).orElse(new ArrayList<>()).get(0).getRooms();

        // 房型落库
        Optional.ofNullable(Optional.ofNullable(rooms).orElse(new Rooms()).getRoom())
                .orElse(new ArrayList<>())
                .forEach(room -> {
                    if (room == null) {
                        return;
                    }
                    HtRoomTypeStdInfoCnbe htRoomTypeStdInfoCnbe = new HtRoomTypeStdInfoCnbe();
                    htRoomTypeStdInfoCnbe.setHotelId(hotelId);
                    htRoomTypeStdInfoCnbe.setRoomTypeCode(room.getRoomId());
                    htRoomTypeStdInfoCnbe.setRoomTypeName(room.getRoomName().replaceAll("&lt;", "<" )
                            .replaceAll("&gt;", ">" ));
                    htRoomTypeStdInfoCnbe.setFloor(room.getFloor());
                    Integer bedType = null;
                    if (StringUtils.isNotBlank(room.getBedType())) {
                        switch (room.getBedType()) {
                            case "大床":
                                bedType = 0;
                                break;
                            case "单人床":
                                bedType = 1;
                                break;
                            case "双床":
                                bedType = 2;
                                break;
                            case "特大床":
                                bedType = 3;
                                break;
                            case "双人床":
                                bedType = 4;
                                break;
                            default:
                                bedType = 5;
                                break;
                        }
                    }
                    htRoomTypeStdInfoCnbe.setBedType(bedType);
                    Date start = new Date();
                    Date end = DateUtils.addDays(start, 15);
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd" );
                    htRoomTypeStdInfoCnbe.setRateplanId(getRateplanId(room.getRoomId(), "USD", hotelId, simpleDateFormat.format(start),
                            simpleDateFormat.format(end), infoDTO));
                    htRoomTypeStdInfoCnbeService.saveOrUpdate(htRoomTypeStdInfoCnbe,
                            new LambdaQueryWrapper<HtRoomTypeStdInfoCnbe>()
                                    .eq(HtRoomTypeStdInfoCnbe::getHotelId,
                                            htRoomTypeStdInfoCnbe.getHotelId())
                                    .eq(HtRoomTypeStdInfoCnbe::getRoomTypeCode,
                                            htRoomTypeStdInfoCnbe.getRoomTypeCode()));
                });
    }

    @Override
    public void syncCNBEXgAmAllRatesTaskPush() {
        try {
            if (redisUtil.setENx(RedisKeyConstant.HOTEL_CNBE_XG_AM_ALL_RATES_PUSH, PUSH_EXPIRY
                    , RedisKeyConstant.HOTEL_CNBE_XG_AM_ALL_RATES_PUSH)) {
                //国家id ： 香港 0002， 澳门  0004
                List<HtHotelStdInfo> htHotelStdInfos = htHotelStdInfoDao.selectList(
                        new LambdaQueryWrapper<HtHotelStdInfo>()
                                .eq(HtHotelStdInfo::getSourceCode, SourceEnum.CNBEINT.getCode())
                                .eq(HtHotelStdInfo::getSourceStatus, 1)
                                .eq(HtHotelStdInfo::getBookStatus, 1)
                                .in(HtHotelStdInfo::getCountry, countryIds));
                if (!CollectionUtils.isEmpty(htHotelStdInfos)
                        && redisUtil.lGetListSize(RedisKeyConstant.HOTEL_CNBE_XG_AM_ALL_RATES_LIST) <= 0) {
                    redisUtil.leftPushAll(RedisKeyConstant.HOTEL_CNBE_XG_AM_ALL_RATES_LIST,
                            htHotelStdInfos.stream().map(HtHotelStdInfo::getHotelId).collect(Collectors.toList()));
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    @Override
    public void syncCNBEXgAmAllRatesTaskPop(IdentityInfoDTO infoDTO) {
        syncXgAmAllRatesTaskPool.execute(() -> new SyncCNBEXgAmAllRatesTaskThread(infoDTO));
    }

    @Override
    public void syncCNBEXgAmAllRatePlaneTask(String countryId, String currency, String hotelId, String start, String end, IdentityInfoDTO infoDTO) {
        deleteExpiredXgAmAllRatePlaneInfo();

        RoomStatusHotel roomStatusHotel =
                cnBookingService.queryRoomStatus(countryId, currency, cnBookingService.parseHotelId(hotelId), null,
                        null, start, end, infoDTO);
        List<QueryRoomStatusResp.Hotel> hotelList =
                Optional.ofNullable(roomStatusHotel.getHotels()).orElse(new QueryRoomStatusResp.Hotels())
                        .getHotel();
        QueryRoomStatusResp.Rooms rooms =
                Optional.ofNullable(hotelList).orElse(new ArrayList<>()).get(0).getRooms();
        if (null == rooms) {
            return;
        }
        Optional.ofNullable(rooms.getRoom()).orElse(new ArrayList<>()).forEach(roomStatus -> {

            // 获取所有的价格计划
            List<RatePlan> ratePlanList = Optional.ofNullable(roomStatus.getRatePlans()).orElse(new RatePlans()).getRatePlan();
            if (CollectionUtils.isEmpty(ratePlanList)) {
                return;
            }

            // 遍历所有的价格计划
            ratePlanList.forEach(ratePlan -> {
                String ratePlanId = ratePlan.getRatePlanId();

                // 获取房态
                List<QueryRoomStatusResp.Rate> rateList = Optional.ofNullable(roomStatus.getRates()).orElse(new QueryRoomStatusResp.Rates()).getRate();
                if (CollectionUtils.isEmpty(rateList)) {
                    return;
                }
                QueryRoomStatusResp.Rate roomRate = rateList.stream().filter(rate -> ratePlanId.equals(rate.getRatePlanId())).findFirst().orElse(null);

                QueryRoomStatusResp.PriceAndStatus priceAndStatusObj = Optional.ofNullable(roomRate).orElse(new QueryRoomStatusResp.Rate()).getPriceAndStatus();
                if (priceAndStatusObj == null) {
                    return;
                }

                Optional.ofNullable(priceAndStatusObj.getPriceAndStatu())
                        .orElse(new ArrayList<>())
                        .forEach(priceAndStatus -> {
                            // 保存房态
                            if (priceAndStatus == null) {
                                return;
                            }
                            if (priceAndStatus.getDate().replaceAll("/", "-" ).equals(end)) {
                                return;
                            }

                            HtPriceCalendarCnbeint htPriceCalendarCnbeint = new HtPriceCalendarCnbeint();
                            htPriceCalendarCnbeint.setHotelId(hotelId);
                            htPriceCalendarCnbeint.setRoomTypeCode(roomStatus.getRoomId());
                            htPriceCalendarCnbeint.setRoomTypeName(roomStatus.getRoomName().replaceAll("&lt;", "<" )
                                    .replaceAll("&gt;", ">" ));
                            try {
                                htPriceCalendarCnbeint.setCalDate(
                                        new SimpleDateFormat("yyyy/MM/dd" ).parse(priceAndStatus.getDate()));
                                htPriceCalendarCnbeint.setQuota(Integer.valueOf(priceAndStatus.getCount()));
                                htPriceCalendarCnbeint.setCurrencyCode(priceAndStatus.getCurrency());
                                String breakfast = ratePlan.getBreakfast();
                                int breakfastCount = 0;
                                if (StringUtils.isNotBlank(breakfast)) {
                                    switch (breakfast) {
                                        case "011001":
                                            breakfastCount = 0;
                                            break;
                                        case "011002":
                                            breakfastCount = 1;
                                            break;
                                        case "011003":
                                            breakfastCount = 2;
                                            break;
                                        default:
                                            break;
                                    }
                                }
                                htPriceCalendarCnbeint.setProductId(ratePlanId);
                                htPriceCalendarCnbeint.setBreakfastCount(breakfastCount);
                                htPriceCalendarCnbeint.setPurchasePrice(new BigDecimal(priceAndStatus.getPrice()));
                                htPriceCalendarCnbeint.setLastCancelTime(
                                        new SimpleDateFormat("yyyy/MM/dd HH:mm:ss" )
                                                .parse(priceAndStatus.getLastCancelTime()));
                                // 保存房态信息
                                htPriceCalendarCnbeintService.saveOrUpdate(htPriceCalendarCnbeint,
                                        new LambdaQueryWrapper<HtPriceCalendarCnbeint>().eq(HtPriceCalendarCnbeint::getHotelId, hotelId)
                                                .eq(HtPriceCalendarCnbeint::getRoomTypeCode, htPriceCalendarCnbeint.getRoomTypeCode())
                                                .eq(HtPriceCalendarCnbeint::getProductId, htPriceCalendarCnbeint.getProductId())
                                                .eq(HtPriceCalendarCnbeint::getCalDate, htPriceCalendarCnbeint.getCalDate()));
                            } catch (Exception e) {
                                log.error("syncCNBEXgAmRatePlaneTask error", e);
                            }
                        });
            });
        });
    }

    private void deleteExpiredXgAmAllRatePlaneInfo() {
        LambdaQueryWrapper<HtPriceCalendarCnbeint> breakfastCountLambdaQueryWrapper = new LambdaQueryWrapper<>();
        breakfastCountLambdaQueryWrapper.le(HtPriceCalendarCnbeint::getCalDate, DateUtils.addDays(new Date(), -1));
        htPriceCalendarCnbeintService.getBaseMapper().delete(breakfastCountLambdaQueryWrapper);
    }

    /**
     * 获取价格计划
     */
    private String getRateplanId(String roomId, String currency, String hotelId, String start, String
            end, IdentityInfoDTO infoDTO) {
        RoomStatusHotel roomStatusHotel =
                cnBookingService.queryRoomStatus(null, currency, cnBookingService.parseHotelId(hotelId), null,
                        null, start, end, infoDTO);
        List<QueryRoomStatusResp.Hotel> hotelList =
                Optional.ofNullable(roomStatusHotel.getHotels()).orElse(new QueryRoomStatusResp.Hotels())
                        .getHotel();
        QueryRoomStatusResp.Rooms rooms =
                Optional.ofNullable(hotelList).orElse(new ArrayList<>()).get(0).getRooms();
        if (null == rooms) {
            return "";
        }
        if ("0".equals(rooms.getRoomCount())) {
            return "";
        }
        String resRatePlanId = "";
        // 根据房型得到对应的房态
        List<QueryRoomStatusResp.Room> roomStatusList = rooms.getRoom().stream()
                .filter(room -> room.getRoomId().equals(roomId))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(roomStatusList)) {
            return "";
        }
        QueryRoomStatusResp.Room roomStatus = roomStatusList.get(0);
        // 筛选rp
        if ("0".equals(roomStatus.getRatePlans().getRatePlanCount())) {
            return "";
        }
        /**
         * 1.预定天数=0
         * 2.最少预定天数=1
         * 3.最少预定间数=1
         * 4.1<入住人数<=3
         * 5.全天可定，最后预定时间 = 23:59
         * 6.国籍不限 013001
         */
        List<RatePlan> ratePlanList = roomStatus.getRatePlans().getRatePlan().stream()
                .filter(ratePlan -> "0".equals(ratePlan.getCutOffDay())
                        && "1".equals(ratePlan.getMinDay())
                        && "1".equals(ratePlan.getMinRoomCount())
                        && Integer.parseInt(ratePlan.getPersonCount()) <= 3
                        && Integer.parseInt(ratePlan.getPersonCount()) > 1
                        && "23:59".equals(ratePlan.getCutOffTime())
                        && "013001".equals(ratePlan.getNationality()))
                .sorted(Comparator.comparing(RatePlan::getPersonCount)) //根据入住人升序
                .collect(Collectors.toList());
        // 根据rp去看房态是否>0
        if ("0".equals(roomStatus.getRates().getRateCount())) {
            return "";
        }
        // 遍历房态
        for (RatePlan ratePlan : ratePlanList) {
            String ratePlanId = ratePlan.getRatePlanId();
            Rate rateStatus = roomStatus.getRates().getRate().stream()
                    .filter(rate -> rate.getRatePlanId().equals(ratePlanId))
                    .findFirst().orElse(null);
            if (rateStatus == null) {
                continue;
            }
            // 判断房态数量>0
            if ("0".equals(rateStatus.getPriceAndStatus().getPriceAndStatuCount())) {
                continue;
            }
            // 找到直接返回
            return rateStatus.getRatePlanId();
        }
        return resRatePlanId;
    }

    private HtHotelStdInfo buildHotelBaseDate(Hotel hotel, HotelsData hotelsData) {
        HtHotelStdInfo info = new HtHotelStdInfo();
        info.setHotelId(cnBookingService.parseInnId(hotel.getHotelId()));
        if ("0001".equals(hotel.getCountryId())) {
            // 区分国内和国外sourceCode ，0001是中国
            info.setSourceCode(SourceEnum.CNBE.getCode());
            info.setHotelName(hotel.getHotelName());
            info.setHotelAddress(hotel.getAddress());
        } else {
            info.setSourceCode(SourceEnum.CNBEINT.getCode());
            info.setHotelNameEn(hotel.getHotelName());
            info.setHotelAddressEn(hotel.getAddress());
            if (Objects.nonNull(hotelsData)
                    && Objects.nonNull(hotelsData.getHotels())
                    && CollectionUtils.isNotEmpty(hotelsData.getHotels().getHotelList())) {
                Hotel tempHotel =
                        hotelsData.getHotels().getHotelList().stream()
                                .filter(e -> e.getHotelId().equals(hotel.getHotelId()))
                                .findFirst().orElse(new Hotel());
                info.setHotelName(tempHotel.getHotelName());
                info.setHotelAddress(tempHotel.getAddress());
            }
        }
        info.setHotelTel(hotel.getReserve2());
        info.setProvinceName(hotel.getProvinceName());
        info.setCityName(hotel.getCityName());
        info.setCountry(hotel.getCountryId());
        info.setCityCode(hotel.getCityId());
        info.setIntroduction(hotel.getIntro());
        info.setLatitude(new BigDecimal(hotel.getLat()));
        info.setLongitude(new BigDecimal(hotel.getLon()));
        info.setMapPointType(0);
        return info;
    }

    private void updateQuota(String hotelId) {
        LambdaUpdateWrapper<HtPriceCalendarCnbe> lambdaUpdateWrapper = new LambdaUpdateWrapper<HtPriceCalendarCnbe>()
                .set(HtPriceCalendarCnbe::getQuota, 0)
                .eq(HtPriceCalendarCnbe::getHotelId, hotelId);
        htPriceCalendarCnbeService.update(lambdaUpdateWrapper);
    }
}

